package com.enba.integrate.justauth.controller;

import com.alibaba.fastjson.JSONObject;
import com.enba.integrate.justauth.cache.AuthStateRedisCache;
import com.enba.integrate.justauth.custom.AuthMyGitlabRequest;
import com.enba.integrate.justauth.resp.Response;
import com.enba.integrate.justauth.service.UserService;
import com.xkcoding.http.config.HttpConfig;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.scope.AuthBaiduScope;
import me.zhyd.oauth.enums.scope.AuthCodingScope;
import me.zhyd.oauth.enums.scope.AuthFacebookScope;
import me.zhyd.oauth.enums.scope.AuthGiteeScope;
import me.zhyd.oauth.enums.scope.AuthGithubScope;
import me.zhyd.oauth.enums.scope.AuthGitlabScope;
import me.zhyd.oauth.enums.scope.AuthGoogleScope;
import me.zhyd.oauth.enums.scope.AuthHuaweiScope;
import me.zhyd.oauth.enums.scope.AuthMicrosoftScope;
import me.zhyd.oauth.enums.scope.AuthWeiboScope;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthAlipayRequest;
import me.zhyd.oauth.request.AuthAliyunRequest;
import me.zhyd.oauth.request.AuthBaiduRequest;
import me.zhyd.oauth.request.AuthCodingRequest;
import me.zhyd.oauth.request.AuthDingTalkRequest;
import me.zhyd.oauth.request.AuthDouyinRequest;
import me.zhyd.oauth.request.AuthElemeRequest;
import me.zhyd.oauth.request.AuthFacebookRequest;
import me.zhyd.oauth.request.AuthFeishuRequest;
import me.zhyd.oauth.request.AuthGiteeRequest;
import me.zhyd.oauth.request.AuthGithubRequest;
import me.zhyd.oauth.request.AuthGitlabRequest;
import me.zhyd.oauth.request.AuthGoogleRequest;
import me.zhyd.oauth.request.AuthHuaweiRequest;
import me.zhyd.oauth.request.AuthKujialeRequest;
import me.zhyd.oauth.request.AuthLinkedinRequest;
import me.zhyd.oauth.request.AuthMeituanRequest;
import me.zhyd.oauth.request.AuthMiRequest;
import me.zhyd.oauth.request.AuthMicrosoftRequest;
import me.zhyd.oauth.request.AuthOschinaRequest;
import me.zhyd.oauth.request.AuthPinterestRequest;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRenrenRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthStackOverflowRequest;
import me.zhyd.oauth.request.AuthTaobaoRequest;
import me.zhyd.oauth.request.AuthTeambitionRequest;
import me.zhyd.oauth.request.AuthToutiaoRequest;
import me.zhyd.oauth.request.AuthTwitterRequest;
import me.zhyd.oauth.request.AuthWeChatEnterpriseQrcodeRequest;
import me.zhyd.oauth.request.AuthWeChatMpRequest;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import me.zhyd.oauth.request.AuthWeiboRequest;
import me.zhyd.oauth.request.AuthXmlyRequest;
import me.zhyd.oauth.utils.AuthScopeUtils;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
 * @version 1.0
 * @website https://www.zhyd.me
 * @date 2019/2/19 9:28
 * @since 1.8
 */
@Slf4j
@Controller
@RequestMapping("/oauth")
public class RestAuthController {

  @Autowired private AuthStateRedisCache stateRedisCache;
  @Autowired private UserService userService;

  @RequestMapping("/render/{source}")
  @ResponseBody
  public void renderAuth(@PathVariable("source") String source, HttpServletResponse response)
      throws IOException {
    log.info("进入render：" + source);
    AuthRequest authRequest = getAuthRequest(source);
    String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
    log.info(authorizeUrl);
    response.sendRedirect(authorizeUrl);
  }

  /** oauth平台中配置的授权回调地址，以本项目为例，在创建github授权应用时的回调地址应为：http://127.0.0.1:8036/oauth/callback/gitee */
  @RequestMapping("/callback/{source}")
  public ModelAndView login(
      @PathVariable("source") String source, AuthCallback callback, HttpServletRequest request) {
    log.info("进入callback：" + source + " callback params：" + JSONObject.toJSONString(callback));
    AuthRequest authRequest = getAuthRequest(source);
    AuthResponse<AuthUser> response = authRequest.login(callback);
    log.info(JSONObject.toJSONString(response));

    if (response.ok()) {
      userService.save(response.getData());
      return new ModelAndView("redirect:/users");
    }

    Map<String, Object> map = new HashMap<>(1);
    map.put("errorMsg", response.getMsg());

    return new ModelAndView("error", map);
  }

  @RequestMapping("/revoke/{source}/{uuid}")
  @ResponseBody
  public Response revokeAuth(
      @PathVariable("source") String source, @PathVariable("uuid") String uuid) throws IOException {
    AuthRequest authRequest = getAuthRequest(source.toLowerCase());

    AuthUser user = userService.getByUuid(uuid);
    if (null == user) {
      return Response.error("用户不存在");
    }
    AuthResponse<AuthToken> response = null;
    try {
      response = authRequest.revoke(user.getToken());
      if (response.ok()) {
        userService.remove(user.getUuid());
        return Response.success("用户 [" + user.getUsername() + "] 的 授权状态 已收回！");
      }
      return Response.error("用户 [" + user.getUsername() + "] 的 授权状态 收回失败！" + response.getMsg());
    } catch (AuthException e) {
      return Response.error(e.getErrorMsg());
    }
  }

  @RequestMapping("/refresh/{source}/{uuid}")
  @ResponseBody
  public Object refreshAuth(
      @PathVariable("source") String source, @PathVariable("uuid") String uuid) {
    AuthRequest authRequest = getAuthRequest(source.toLowerCase());

    AuthUser user = userService.getByUuid(uuid);
    if (null == user) {
      return Response.error("用户不存在");
    }
    AuthResponse<AuthToken> response = null;
    try {
      response = authRequest.refresh(user.getToken());
      if (response.ok()) {
        user.setToken(response.getData());
        userService.save(user);
        return Response.success(
            "用户 ["
                + user.getUsername()
                + "] 的 access token 已刷新！新的 accessToken: "
                + response.getData().getAccessToken());
      }
      return Response.error(
          "用户 [" + user.getUsername() + "] 的 access token 刷新失败！" + response.getMsg());
    } catch (AuthException e) {
      return Response.error(e.getErrorMsg());
    }
  }

  /**
   * 根据具体的授权来源，获取授权请求工具类 redirectUri填写的地址需要让外网访问，可配置域名或ip
   *
   * @param source
   * @return
   */
  private AuthRequest getAuthRequest(String source) {
    AuthRequest authRequest = null;
    switch (source.toLowerCase()) {
      case "dingtalk":
        authRequest =
            new AuthDingTalkRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/dingtalk")
                    .build());
        break;
      case "baidu":
        authRequest =
            new AuthBaiduRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/baidu")
                    .scopes(
                        Arrays.asList(
                            AuthBaiduScope.BASIC.getScope(),
                            AuthBaiduScope.SUPER_MSG.getScope(),
                            AuthBaiduScope.NETDISK.getScope()))
                    //                        .clientId("")
                    //                        .clientSecret("")
                    //
                    // .redirectUri("http://localhost:9001/oauth/baidu/callback")
                    .build());
        break;
      case "github":
        authRequest =
            new AuthGithubRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/github")
                    .scopes(AuthScopeUtils.getScopes(AuthGithubScope.values()))
                    // 针对国外平台配置代理
                    .httpConfig(
                        HttpConfig.builder()
                            .timeout(15000)
                            .proxy(
                                new Proxy(
                                    Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                            .build())
                    .build(),
                stateRedisCache);
        break;
      case "gitee":
        authRequest =
            new AuthGiteeRequest(
                AuthConfig.builder()
                    .clientId("a5330bc9807b58fe4e40d367eb48e592fa8e808c396dd59829318e83e8fce5d9")
                    .clientSecret(
                        "84559a0914d9e002f9f6426af6d7243d6660dbae1c5c515de5815d42e430f369")
                    .redirectUri("http://tb2mnd.natappfree.cc/oauth/callback/gitee")
                    .scopes(AuthScopeUtils.getScopes(AuthGiteeScope.values()))
                    .build(),
                stateRedisCache);
        break;
      case "weibo":
        authRequest =
            new AuthWeiboRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/weibo")
                    .scopes(
                        Arrays.asList(
                            AuthWeiboScope.EMAIL.getScope(),
                            AuthWeiboScope.FRIENDSHIPS_GROUPS_READ.getScope(),
                            AuthWeiboScope.STATUSES_TO_ME_READ.getScope()))
                    .build());
        break;
      case "coding":
        authRequest =
            new AuthCodingRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/coding")
                    .domainPrefix("")
                    .scopes(
                        Arrays.asList(
                            AuthCodingScope.USER.getScope(),
                            AuthCodingScope.USER_EMAIL.getScope(),
                            AuthCodingScope.USER_PHONE.getScope()))
                    .build());
        break;
      case "oschina":
        authRequest =
            new AuthOschinaRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/oschina")
                    .build());
        break;
      case "alipay":
        // 支付宝在创建回调地址时，不允许使用localhost或者127.0.0.1，所以这儿的回调地址使用的局域网内的ip
        authRequest =
            new AuthAlipayRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .alipayPublicKey("")
                    .redirectUri("https://www.zhyd.me/oauth/callback/alipay")
                    .build());
        break;
      case "qq":
        authRequest =
            new AuthQqRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/qq")
                    .build());
        break;
      case "wechat_open":
        authRequest =
            new AuthWeChatOpenRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://www.zhyd.me/oauth/callback/wechat")
                    .build());
        break;
//      case "csdn":
//        authRequest =
//            new AuthCsdnRequest(
//                AuthConfig.builder()
//                    .clientId("")
//                    .clientSecret("")
//                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/csdn")
//                    .build());
//        break;
      case "taobao":
        authRequest =
            new AuthTaobaoRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/taobao")
                    .build());
        break;
      case "google":
        authRequest =
            new AuthGoogleRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/google")
                    .scopes(
                        AuthScopeUtils.getScopes(
                            AuthGoogleScope.USER_EMAIL,
                            AuthGoogleScope.USER_PROFILE,
                            AuthGoogleScope.USER_OPENID))
                    // 针对国外平台配置代理
                    .httpConfig(
                        HttpConfig.builder()
                            .timeout(15000)
                            .proxy(
                                new Proxy(
                                    Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                            .build())
                    .build());
        break;
      case "facebook":
        authRequest =
            new AuthFacebookRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("https://justauth.cn/oauth/callback/facebook")
                    .scopes(AuthScopeUtils.getScopes(AuthFacebookScope.values()))
                    // 针对国外平台配置代理
                    .httpConfig(
                        HttpConfig.builder()
                            .timeout(15000)
                            .proxy(
                                new Proxy(
                                    Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                            .build())
                    .build());
        break;
      case "douyin":
        authRequest =
            new AuthDouyinRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/douyin")
                    .build());
        break;
      case "linkedin":
        authRequest =
            new AuthLinkedinRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/linkedin")
                    .scopes(null)
                    .build());
        break;
      case "microsoft":
        authRequest =
            new AuthMicrosoftRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/microsoft")
                    .scopes(
                        Arrays.asList(
                            AuthMicrosoftScope.USER_READ.getScope(),
                            AuthMicrosoftScope.USER_READWRITE.getScope(),
                            AuthMicrosoftScope.USER_READBASIC_ALL.getScope(),
                            AuthMicrosoftScope.USER_READ_ALL.getScope(),
                            AuthMicrosoftScope.USER_READWRITE_ALL.getScope(),
                            AuthMicrosoftScope.USER_INVITE_ALL.getScope(),
                            AuthMicrosoftScope.USER_EXPORT_ALL.getScope(),
                            AuthMicrosoftScope.USER_MANAGEIDENTITIES_ALL.getScope(),
                            AuthMicrosoftScope.FILES_READ.getScope()))
                    .build());
        break;
      case "mi":
        authRequest =
            new AuthMiRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/mi")
                    .build());
        break;
      case "toutiao":
        authRequest =
            new AuthToutiaoRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/toutiao")
                    .build());
        break;
      case "teambition":
        authRequest =
            new AuthTeambitionRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://127.0.0.1:8036/oauth/callback/teambition")
                    .build());
        break;
      case "pinterest":
        authRequest =
            new AuthPinterestRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("https://eadmin.innodev.com.cn/oauth/callback/pinterest")
                    // 针对国外平台配置代理
                    .httpConfig(
                        HttpConfig.builder()
                            .timeout(15000)
                            .proxy(
                                new Proxy(
                                    Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                            .build())
                    .build());
        break;
      case "renren":
        authRequest =
            new AuthRenrenRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://127.0.0.1:8036/oauth/callback/teambition")
                    .build());
        break;
      case "stack_overflow":
        authRequest =
            new AuthStackOverflowRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("((")
                    .redirectUri("http://localhost:8036/oauth/callback/stack_overflow")
                    .stackOverflowKey("")
                    .build());
        break;
      case "huawei":
        authRequest =
            new AuthHuaweiRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://127.0.0.1:8036/oauth/callback/huawei")
                    .scopes(
                        Arrays.asList(
                            AuthHuaweiScope.BASE_PROFILE.getScope(),
                            AuthHuaweiScope.MOBILE_NUMBER.getScope(),
                            AuthHuaweiScope.ACCOUNTLIST.getScope(),
                            AuthHuaweiScope.SCOPE_DRIVE_FILE.getScope(),
                            AuthHuaweiScope.SCOPE_DRIVE_APPDATA.getScope()))
                    .build());
        break;
      case "wechat_enterprise":
        authRequest =
            new AuthWeChatEnterpriseQrcodeRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://justauth.cn/oauth/callback/wechat_enterprise")
                    .agentId("1000003")
                    .build());
        break;
      case "kujiale":
        authRequest =
            new AuthKujialeRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/kujiale")
                    .build());
        break;
      case "gitlab":
        authRequest =
            new AuthGitlabRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/gitlab")
                    .scopes(AuthScopeUtils.getScopes(AuthGitlabScope.values()))
                    .build());
        break;
      case "meituan":
        authRequest =
            new AuthMeituanRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/meituan")
                    .build());
        break;
      case "eleme":
        authRequest =
            new AuthElemeRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://dblog-web.zhyd.me/oauth/callback/eleme")
                    .build());
        break;
      case "mygitlab":
        authRequest =
            new AuthMyGitlabRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://127.0.0.1:8036/oauth/callback/mygitlab")
                    .build());
        break;
      case "twitter":
        authRequest =
            new AuthTwitterRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("https://threelogin.31huiyi.com/oauth/callback/twitter")
                    // 针对国外平台配置代理
                    .httpConfig(
                        HttpConfig.builder()
                            .timeout(15000)
                            .proxy(
                                new Proxy(
                                    Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                            .build())
                    .build());
        break;
      case "wechat_mp":
        authRequest =
            new AuthWeChatMpRequest(
                AuthConfig.builder().clientId("").clientSecret("").redirectUri("").build());
        break;
      case "aliyun":
        authRequest =
            new AuthAliyunRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/aliyun")
                    .build());
        break;
      case "xmly":
        authRequest =
            new AuthXmlyRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/xmly")
                    .build());
        break;
      case "feishu":
        authRequest =
            new AuthFeishuRequest(
                AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("http://localhost:8036/oauth/callback/feishu")
                    .build());
        break;
      default:
        break;
    }
    if (null == authRequest) {
      throw new AuthException("未获取到有效的Auth配置");
    }
    return authRequest;
  }
}
